#!/usr/bin/env python

__author__ = "Abhinav Sarkar"
__version__ = "0.1"
__license__ = "GNU Lesser General Public License"

class Artist (object):
	"""A class representing an artist.

	The Artist class exposes the following instance properties:

		artist.name
		artist.url
		artist.thumbnail
		artist.image
		artist.streamable
		artist.mbid
		artist.similarArtists
		artist.topFans
		artist.topFan
		artist.topTracks
		artist.topTrack
		artist.topAlbums
		artist.topAlbum
		artist.topTags
		artist.topTag
		artist.currentEvents
		
	The Artist class exposes the following instance methods:
		
		artist.AsDict()
		artist.GetCurrentEvents()
		artist.GetSimilarArtists()
		artist.GetTopAlbums()
		artist.GetTopFans()
		artist.GetTopTags()
		artist.GetTopTracks()
	"""
	def __init__(self,
				 name = None,
				 url = None,
				 thumbnail = None,
				 image = None,
				 streamable = None):
		"""An object to hold an artist.

		This class is normally instantiated by the Webservice class.

		Args:
			name: name of this artist
			url: URL of this artist on last.fm
			thumbnail: URL of the thumbnail image of this artist on last.fm
			image: URL of the image of this artist on last.fm
			streamable:
				a boolean value indicating whether this artist is
				streamable from last.fm

		Usage:
			>>> import audioscrobblerws
			>>> ws = audioscrobblerws.Webservice()
			>>> artist = ws.GetArtist('bob dylan')
		"""
		self.name = name
		self.url = url
		self.thumbnail = thumbnail
		self.image = image
		self.streamable = streamable

	def GetSimilarArtists(self):
		"""Get the artists similar to this artists.

		Returns:
			An ordered list of Artist objects corresponding to the artists similar
			to this artist.

		Usage:
			>>> artist = ws.GetArtist('bob dylan')
			>>> simArtists = artist.GetSimilarArtists()
			>>> [a.name for a in simArtists[:10]]
			['Bob Dylan and The Band', 'Neil Young', 'The Band',
			'The Rolling Stones', 'Bob Dylan & Johnny Cash', 'The Velvet Underground',
			'Van Morrison', 'Leonard Cohen', 'John Lennon', 'Bruce Springsteen']
		"""
		if not self.name:
			raise AudioscrobblerError("artist name has to be set before calling this method")
		url = "http://ws.audioscrobbler.com/1.0/artist/%s/similar.xml" % self.name
		xml = Webservice()._FetchUrl(url)
		data = Xml2Dict(ElementTree.XML(xml))

		self.name = data['@artist']
		if '@mbid' in data:
			self.mbid = data['@mbid']
		self.image = data['@picture']
		self.streamable = data['@streamable'] == '1'

		artists = []
		if not 'artist' in data:
			return artists

		for artistData in data['artist']:
			artist = Artist(
				name = artistData['name'],
				url = artistData['url'],
				thumbnail = artistData['image_small'],
				image = artistData['image'],
				streamable = artistData['streamable'] == '1'
			)
			if 'mbid' in artistData:
				artist.mbid = artistData['mbid']
			artist.match = float(artistData['match'])
			artists.append(artist)
		return artists

	similarArtists = property(GetSimilarArtists,
		doc = "Artists similar to this artist")

	def GetTopFans(self):
		"""Get the top fans for this artist.

		Returns:
			An ordered list of User objects corresponding to this artist's top fans.

		Usage:
			>>> artist = ws.GetArtist('bob dylan')
			>>> fans = artist.GetTopFans()
			>>> [f.username for f in fans[:10]]
			['cgab34', 'ncounter', 'steponmehiney', 'ladybug570', 'Giusseppe',
			'Lassekl', 'seethroughdog', 'joakimhu', 'HadleyJane', 'yoyocrispy']
		"""
		if not self.name:
			raise AudioscrobblerError("artist name has to be set before calling this method")
		url = "http://ws.audioscrobbler.com/1.0/artist/%s/fans.xml" % self.name
		xml = Webservice()._FetchUrl(url)
		data = Xml2Dict(ElementTree.XML(xml))

		users = []
		if not 'user' in data:
			return users

		for userData in data['user']:
			user = User(username = userData['@username'],
						avatar = userData['image'],
						url = userData['url'])
			user.weight = int(userData['weight'])
			users.append(user)
		return users

	topFans = property(GetTopFans,
		doc = "Top fans of this artist")
	topFan = property(lambda (self): len(self.GetTopFans()) and self.GetTopFans()[0] or None,
		doc = "Topmost fan of this artist")

	def GetTopTracks(self):
		"""Get the top tracks for this artist.

		Returns:
			An ordered list of Track objects corresponding to this artist's
			top tracks.

		Usage:
			>>> artist = ws.GetArtist('bob dylan')
			>>> tracks = artist.GetTopTracks()
			>>> [t.name for t in tracks[:10]]
			['Like a Rolling Stone', "Blowin' in the Wind", 'Mr. Tambourine Man',
			'Subterranean Homesick Blues', 'Just Like a Woman', 'Tangled Up in Blue',
			'I Want You', 'All Along the Watchtower', 'Hurricane', "The Times They Are A-Changin'"]
		"""
		if not self.name:
			raise AudioscrobblerError("artist name has to be set before calling this method")
		url = "http://ws.audioscrobbler.com/1.0/artist/%s/toptracks.xml" % self.name
		xml = Webservice()._FetchUrl(url)
		data = Xml2Dict(ElementTree.XML(xml))

		tracks = []
		if not 'track' in data:
			return tracks

		for trackData in data['track']:
			track = Track(
				artist = self,
				name = trackData['name'],
				url = trackData['url']
			)

			if 'mbid' in trackData:
				track.mbid = trackData['mbid']

			track.reach = int(trackData['reach'])
			tracks.append(track)
		return tracks

	topTracks = property(GetTopTracks,
		doc = "Top tracks for this artist")
	topTrack = property(lambda (self): len(self.GetTopTracks()) and self.GetTopTracks()[0] or None,
		doc = "Topmost track for this artist")

	def GetTopAlbums(self):
		"""Get the top albums for this artist.

		Returns:
			An ordered list of Album objects corresponding to this artist's
			top albums.

		Usage:
			>>> artist = ws.GetArtist('bob dylan')
			>>> albums = artist.GetTopAlbums()
			>>> [a.name for a in albums[:5]]
			['Bob Dylan: The Collection', 'Highway 61 Revisited', 'Blonde on Blonde',
			'Blood on the Tracks', 'Bringing It All Back Home']
		"""
		if not self.name:
			raise AudioscrobblerError("artist name has to be set before calling this method")
		url = "http://ws.audioscrobbler.com/1.0/artist/%s/topalbums.xml" % self.name
		xml = Webservice()._FetchUrl(url)
		data = Xml2Dict(ElementTree.XML(xml))

		albums = []
		if not 'album' in data:
			return albums

		for albumData in data['album']:
			album = Album(
				artist = self,
				name = albumData['name'],
				url = albumData['url'],
				coverart = albumData['image']
			)

			if 'mbid' in albumData:
				album.mbid = albumData['mbid']

			album.reach = int(albumData['reach'])
			albums.append(album)
		return albums

	topAlbums = property(GetTopAlbums,
		doc = "Top albums for this artist")
	topAlbum = property(lambda (self): len(self.GetTopAlbums()) and self.GetTopAlbums()[0] or None,
		doc = "Topmost album for this artist")

	def GetTopTags(self):
		"""Get the top tags for this artist.

		Returns:
			An ordered list of Tag objects corresponding to this artist's
			top tags.

		Usage:
			>>> artist = ws.GetArtist('bob dylan')
			>>> tags = artist.GetTopTags()
			>>> [t.name for t in tags[:10]]
			['folk', 'singer-songwriter', 'classic rock', 'rock',
			'folk rock', 'blues', '60s', 'Bob Dylan', 'acoustic', 'folk-rock']
		"""
		if not self.name:
			raise AudioscrobblerError("artist name has to be set before calling this method")
		url = "http://ws.audioscrobbler.com/1.0/artist/%s/toptags.xml" % self.name
		xml = Webservice()._FetchUrl(url)
		data = Xml2Dict(ElementTree.XML(xml))

		tags = []
		if not 'tag' in data:
			return tags

		for tagData in data['tag']:
			tag = Tag(
				name = tagData['name'],
				url = tagData['url'],
			)

			tag.count = int(tagData['count'])
			tags.append(tag)
		return tags

	topTags = property(GetTopTags,
		doc = "Top tags for this artist")
	topTag = property(lambda (self): len(self.GetTopTags()) and self.GetTopTags()[0] or None,
		doc = "Topmost tag for this artist")

	def GetCurrentEvents(self):
		"""Get the current events this artist is marked as playing on.

		Returns:
			A list of Event objects corresponding to the events this
			artist is marked as playing on.

		Usage:
			>>> artist = ws.GetArtist('bob dylan')
			>>> events = artist.GetCurrentEvents()
			>>> for event in events[:5]:
			...     event.venue
			...
			u'\u010cEZ arena, Rusk\xe1 3077/135, 70030 Ostrava, Czech Republic'
			u'Stadthalle, Vogelweidplatz 14, 1150 Wien, Austria'
			u'Salzburgarena, Salzburg, Austria'
			u'Stadion "Varteks", Vara\u017edin, Croatia'
			u'Palazzo Alberi, Trento, Italy'
		"""
		if not self.name:
			raise AudioscrobblerError("artist name has to be set before calling this method")
		url = "http://ws.audioscrobbler.com/1.0/artist/%s/events.ics" % self.name

		return Event.fromICal(url)

	currentEvents = property(GetCurrentEvents,
		doc = "Current events this artist is marked as playing on")

	def AsDict(self):
		"""Returns self as a dict"""
		return self.__dict__

	def __eq__(self, other):
		return self.name == other.name

	def __ne__(self, other):
		return not self.__eq__(other)

import cElementTree as ElementTree

from album import Album
from xml2dict import Xml2Dict
from webservice import Webservice
from tag import Tag
from track import Track
from user import User
from event import Event
from error import AudioscrobblerError
